package frontend.lexer;

import exceptions.LexerInnerException;
import frontend.cehandler.compilationerrors.CECause;
import frontend.cehandler.compilationerrors.CEType;
import frontend.cehandler.CEHandler;
import frontend.lexer.token.IntConToken;
import frontend.lexer.token.StrConToken;
import frontend.lexer.token.Token;
import frontend.lexer.token.TokenType;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Scanner;

public class LexerSupporter {
    private final Lexer lexer;
    private int pos = 0;
    private int lineNum = 1;
    private int lineStart = -1;
    private final StringBuilder sourceCode = new StringBuilder();
    private final Scanner scanner;
    private final PrintWriter printWriter;
    private final PrintWriter debugWriter;
    private final CEHandler ceHandler;
    private final ArrayList<String> sourceCodeList;

    LexerSupporter(Lexer lexer) {
        this.lexer = lexer;
        this.scanner = lexer.scanner;
        this.sourceCodeList = lexer.sourceCodeList;
        this.printWriter = lexer.printWriter;
        this.debugWriter = lexer.debugWriter;
        this.ceHandler = lexer.ceHandler;
    }

    private boolean update(int pos) {
        if (sourceCode.length() > pos) {
            return true;
        }
        while(scanner.hasNextLine()) {
            String next = scanner.nextLine();
            sourceCodeList.add(next);
            sourceCode.append(next);
            sourceCode.append('\n');
            if (sourceCode.length() > pos) {
                return true;
            }
        }
        return false;
    }

    char now() {
        return next(0);
    }
    boolean hasNow() {
        return hasNext(0);
    }
    boolean hasNext() {
        return hasNext(1);
    }
    boolean hasNext(int n) {
        return update(pos + n);
    }
    char next() {
        return next(1);
    }
    char next(int n) {
        if (!hasNext(n)) {
            return '\0';
        }
        return sourceCode.charAt(pos + n);
    }
    void push() {
        push(1);
    }
    void push(int n) {
        pos += n;
    }

    int linePos() {
        return pos - lineStart;
    }
    int lineNum() {
        return lineNum;
    }

    ArrayList<String> getSourceCodeList() {
        return sourceCodeList;
    }

    /**
     * 已遇到换行符，更新状态
     * 前置条件：已遇到换行符，pos指向该换行符。
     * 后置条件：更新pos、lineNum、lineStart。
     */
    void anotherLine() {
        if (now() != '\n') throw new LexerInnerException();
        lineNum++;
        lineStart = pos;
    }

    Token makeToken(TokenType tokenType, int lineNum, int linePosStart, int linePosEnd) {
        return new Token(
                tokenType,
                sourceCodeList.get(lineNum - 1).substring(linePosStart - 1, linePosEnd - 1),
                lineNum,
                sourceCodeList.get(lineNum - 1),
                linePosStart,
                linePosEnd);
    }

    IntConToken makeToken(TokenType tokenType, int intVal, int lineNum, int linePosStart, int linePosEnd) {
        return new IntConToken(
                tokenType,
                sourceCodeList.get(lineNum - 1).substring(linePosStart - 1, linePosEnd - 1),
                intVal,
                lineNum,
                sourceCodeList.get(lineNum - 1),
                linePosStart,
                linePosEnd);
    }

    StrConToken makeToken(TokenType tokenType, String stringVal, ArrayList<StrConToken.StrConItem> strConItems, int lineNum, int linePosStart, int linePosEnd) {
        return new StrConToken(
                tokenType,
                sourceCodeList.get(lineNum - 1).substring(linePosStart - 1, linePosEnd - 1),
                stringVal,
                strConItems,
                lineNum,
                sourceCodeList.get(lineNum - 1),
                linePosStart,
                linePosEnd);
    }
}
